﻿// Imports
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;

namespace Nickel
{

    /// <summary>
    /// Handles search
    /// </summary>
    public class Search : Page
    {

        /// <summary>
        /// Handles page load
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        protected void Page_Load(object sender, EventArgs e)
        {

            // Search requested
            if (Request.Form[Constants.TYPE_SEARCH] != null)
            {

                // Format search string
                string strSearch = Request.Form[Constants.INPUT_SEARCH].Trim().ToLower();

                // Check length
                if (strSearch.Length > 0)
                {

                    // Split search string to array
                    string terms = String.Join("|", strSearch.ToLower().Replace("|", "\\|").Split(" ".ToCharArray()));

                    // Initialize a search result objects
                    List<SearchResult> results = new List<SearchResult>();
                    SearchResult result = null;

                    // Create RegEx
                    Regex search = new Regex(terms, RegexOptions.IgnoreCase);
                    MatchCollection matches = null;

                    // Get signed in user
                    Author user = Account.GetSignedInAuthor(Data, Request.Cookies[Settings.COOKIE_NAME]);

                    // Iterate through articles
                    foreach (Article article in Data.Articles)
                    {

                        // Only show published articles to non-admins
                        if (article.Published || (user != null && user.Admin))
                        {

                            // Terms occurs in title string
                            matches = search.Matches(article.Title);
                            if (matches != null && matches.Count > 0)
                            {

                                // Initialize result if needed and update count
                                if (result == null)
                                    result = new SearchResult(Data, article);
                                result.Count += matches.Count;
                                matches = null;

                            }

                            // Terms occurs in subtitle string
                            matches = search.Matches(article.SubTitle);
                            if (matches != null && matches.Count > 0)
                            {

                                // Initialize result if needed and update count
                                if (result == null) 
                                    result = new SearchResult(Data, article);
                                result.Count += matches.Count;
                                matches = null;

                            }

                            // Match in text
                            matches = search.Matches(article.Text);
                            if (matches != null && matches.Count > 0)
                            {

                                // Initialize result if needed and update count
                                if (result == null) 
                                    result = new SearchResult(Data, article);
                                result.Count += matches.Count;
                                matches = null;

                            }

                            // Iterate through terms
                            foreach (string term in strSearch.Split(" ".ToCharArray()))
                            {

                                // Match in keyword
                                if (article.HasKeyword(term))
                                {

                                    // Initialize result if needed and update count
                                    if (result == null) 
                                        result = new SearchResult(Data, article);
                                    result.Count++;

                                }

                                // Match in tag
                                if (article.HasTag(term))
                                {

                                    // Initialize result if needed and update count
                                    if (result == null) 
                                        result = new SearchResult(Data, article);
                                    result.Count++;

                                }

                            }

                            // Iterate through comments
                            foreach (Comment comment in article.Comments)
                            {

                                // Match in text
                                matches = search.Matches(comment.Text);
                                if (matches != null && matches.Count > 0)
                                {

                                    // Initialize result if needed and update count
                                    if (result == null) 
                                        result = new SearchResult(Data, article);
                                    result.Count += matches.Count;
                                    matches = null;

                                }

                            }

                            // Result found
                            if (result != null)
                            {

                                // Find matches in display text
                                matches = search.Matches(result.Text);
                                if (matches != null && matches.Count > 0)
                                {

                                    // Find start posotion of display text
                                    int start = matches[0].Index - 30;
                                    if (start < 0) 
                                        start = 0;

                                    // Truncate first part of text
                                    if (start > 30) 
                                        result.Text = "..." + result.Text.Substring(start, result.Text.Length - start);

                                }

                                // Truncate text
                                if (result.Text.Length > 200) 
                                    result.Text = result.Text.Substring(0, 200).Trim() + "...";

                                // Highlight search terms
                                result.Text = search.Replace(result.Text, new MatchEvaluator(HighlightTerm));

                                // Add result
                                results.Add(result);
                                result = null;

                            }

                        }

                    }

                    // No results
                    if (results.Count < 1)
                    {

                        // Write error
                        Response.Write("{ \"" + Constants.FIELD_ERROR + "\": \"" + StringHelper.JsonHtml(Errors.SEARCH_NO_RESULTS) + "\" }");

                    }
                    else
                    {

                        // Sort results by count
                        results.Sort(Sort.SearchResultByCount);

                        // Initialize result string
                        string strResults = "<p>Your search returned " + results.Count.ToString() + " result";
                        if (results.Count > 1) strResults += "s";
                        strResults += ".</p>";

                        // Iterate through results
                        foreach (SearchResult searchResult in results)
                        {

                            // Add result HTML
                            strResults += "<div class=\"searchResult\"><p><strong><a href=\"" + strBaseUrl + searchResult.Url +
                                "\">" + searchResult.Title + "</a></strong> " +
                                "- " + searchResult.SubTitle + " " +
                                "<em>(" + searchResult.Count.ToString() + " " + 
                                (searchResult.Count == 1 ? "match" : "matches") + ")</em>" +
                                "<span class=\"tiny\">&nbsp;<br />" + searchResult.Author +
                                " / " + searchResult.ModifiedDateTime + "</span>" +
                                "<div class=\"resultText\">" + searchResult.Text + "</div>" +
                                "<span class=\"tiny\">Tags: ";

                            // Iterate through tags
                            foreach (string tag in searchResult.Tags)
                            {

                                // Append tag link
                                strResults += "<a href=\"" + strBaseUrl + StringHelper.Encode(tag) +
                                "\" class=\"tiny\">" + tag + "</a>";

                                // Add comma
                                if (tag != searchResult.Tags[searchResult.Tags.Count - 1]) 
                                    strResults += ", ";

                            }

                            // Close result HTML
                            strResults += "</span></p></div>";

                            // Add line break
                            if (searchResult != results[results.Count - 1]) 
                                strResults += "<hr />";

                        }

                        // Write results
                        Response.Write("{ \"" + Constants.FIELD_SUCCESS + "\": \"" + StringHelper.JsonHtml(strResults) + "\" }");

                    }

                    // Clean up
                    matches = null;
                    terms = null;
                    results.Clear();
                    results = null;

                }
                else
                {

                    // Write error
                    Response.Write("{ \"" + Constants.FIELD_ERROR + "\": \"" + StringHelper.JsonHtml(Errors.NOTHING_ENTERED) + "\" }");

                }

            }
            else
            {

                // Write error
                Response.Write("{ \"" + Constants.FIELD_ERROR + "\": \"" + StringHelper.JsonHtml(Errors.NOTHING_ENTERED) + "\" }");

            }

        }

        /// <summary>
        /// Highlight search term
        /// </summary>
        /// <param name="match">Match object</param>
        /// <returns>Highlighted string</returns>
        private string HighlightTerm(Match match)
        {

            // Bold text
            return "<strong>" + match.Value + "</strong>";

        }

    }

}
